package adm

import (
	"database/sql"
	"errors"
	"fmt"
)

type Connection struct {
	name    *string
	isConn  bool
	conn    *sql.DB
	error   error
}

var globalConns = make(map[string]*Connection)

func Conn(name string) *Connection {
	conf := GetConf(name)
	if conf == nil {
		return &Connection{error: errors.New("Can't find config " + name +  "!") }
	}
	conn, exists := globalConns[name]
	if !exists {
		globalConns[name] = &Connection{name: &conf.Name, isConn: false }
		globalConns[name].connect(conf)
		return globalConns[name]
	}
	return conn
}

func (this *Connection) connect(conf *XmlDb) {
	if this.conn != nil { return }
	db, err := sql.Open(conf.DSN, conf.GetConnDSN())
	if err != nil {
		this.error = err
		return
	}
	db.Ping()
	if err != nil {
		this.error = err
		return
	}
	// 先把db绑定，毕竟已经连接上了
	if conf.MaxConn > 0 {
		db.SetMaxOpenConns(conf.MaxConn)
	}
	if conf.MaxIdle > 0 {
		db.SetMaxIdleConns(conf.MaxIdle)
	}
	if _, err := db.Exec("use " + conf.Db); err != nil {
		this.error = err
		return
	}
	this.conn = db
	this.isConn = true
}

func (this *Connection) IsConn() bool {
	return this.isConn
}

func (this *Connection) GetName() string {
	return *this.name
}

func (this *Connection) GetDB() (*sql.DB, error) {
	if !this.IsConn() {
		if this.HasErr() {
			return nil, this.GetErr()
		} else {
			return nil, errors.New(fmt.Sprintf("database %s does not connect", this.GetName()))
		}
	}
	return this.conn, nil
}

func (this *Connection) GetErr() error {
	return this.error
}

func (this *Connection) HasErr() bool {
	return this.error != nil
}

func (this *Connection) Query(sql string, params ...interface{}) *ResultSet {
	if !this.IsConn() {
		if this.HasErr() {
			return NewQueryResult(nil, this.error)
		} else {
			NewQueryResult(nil, errors.New(fmt.Sprintf("database %s does not connect", this.GetName())))
		}
	}
	if len(sql) <= 0 {
		NewQueryResult(nil, errors.New("empty sql!"))
	}
	rows, err := this.conn.Query(sql, params...)
	return NewQueryResult(rows, err)
}

func (this *Connection) Find(query *QueryBuilder) *ResultSet {
	err := query.Verify()
	if err != nil {
		return NewQueryResult(nil, err)
	}
	return this.Query(query.GetSQL(), query.GetParams()...)
}

func (this *Connection) Exec(t QueryType, sql string, params ...interface {}) *ExecResult {
	if !this.IsConn() {
		if this.HasErr() {
			return NewExecResult(t, nil, this.error)
		} else {
			err := errors.New(fmt.Sprintf("database %s does not connect", this.GetName()))
			return NewExecResult(t, nil, err)
		}
	}
	if len(sql) <= 0 {
		return NewExecResult(t, nil, errors.New("empty sql!"))
	}
	stmt, err := this.conn.Prepare(sql)
	if err != nil {
		if stmt != nil {
			stmt.Close()
		}
		return NewExecResult(t, nil, err)
	}
	defer stmt.Close()
	rs, err := stmt.Exec(params...)
	return NewExecResult(t, rs, err)
}

func (this *Connection) execQuery(query *QueryBuilder) *ExecResult {
	err := query.Verify()
	t := query.GetQueryType()
	if err != nil {
		return NewExecResult(t, nil, err)
	}
	sql, params := query.GetExecSQLParams()
	return this.Exec(t, sql, params...)
}

func (this *Connection) Insert(query *QueryBuilder) *ExecResult {
	return this.execQuery(query)
}

func (this *Connection) Update(query *QueryBuilder) *ExecResult {
	return this.execQuery(query)
}

func (this *Connection) Delete(query *QueryBuilder) *ExecResult {
	return this.execQuery(query)
}

//func (this *Connection) QueryRow(query *Query) (*sql.Row) {
//    if !this.IsConn() {
//        return nil
//    }
//    return this.conn.QueryRow(query.GetSQL(), query.GetParams() ...)
//}

